home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
scope
/
001-025
/
scopedisk24
/
qrt14src
/
ray.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-18
|
19KB
|
771 lines
/************************************************************
ray trace module
************************************************************/
#include <math.h>
#include "qrt.h"
/* #define RAYDEBUG 1 */
OBJ_PTR new_line(), new_obj();
/**********************************************************
Diffuse color module. Also compute specular reflections
here for efficiency sake - only one lamp eneumerator
needed that way.
**********************************************************/
DiffColor(color,cinfo,norm,loc,oline)
SVECT_PTR color;
VECT_PTR norm,loc;
CINFO_PTR cinfo;
OBJ_PTR oline;
{
OBJ_PTR lamp, line, CurrObj, Ray_Hit();
float t;
register float dist, t1,t2;
VECTOR refl, atten;
line=new_line();
VectEQ(&(line->loc),loc);
lamp=THEWORLD.lamps;
while (lamp!=NULL) {
# ifdef ROBUST
if (lamp->type!=LAMP) Error(INTERNAL_ERROR,601);
# endif
/* find dir to lamp */
VecSubtract(&(line->vect1),&(lamp->loc),loc);
CurrObj=NULL;
atten.x = atten.y = atten.z = 1.00;
if (def.shadow==TRUE) {
/* set light attenuation factor */
CurrObj=Ray_Hit(THEWORLD.stack,line,&t,TRUE,TRUE,&atten);
THEWORLD.to_lamp++;
}
if (CurrObj==NULL) { /* hit nothing ? */
dist = DotProd((line->vect1),(line->vect1));
Normalize(&(line->vect1));
if (cinfo->diff.r>0 || cinfo->diff.g>0 || cinfo->diff.b>0) {
t1 = DotProd((*norm),(line->vect1));
if (t1>0) {
t2 = lamp->vect1.y*t1/sqrt(dist);
color->r+=(short)((lamp->cinfo.amb.r) * atten.x *
t2*(cinfo->diff.r)/CNUM);
color->g+=(short)((lamp->cinfo.amb.g) * atten.y *
t2*(cinfo->diff.g)/CNUM);
color->b+=(short)((lamp->cinfo.amb.b) * atten.z *
t2*(cinfo->diff.b)/CNUM);
}
}
if (cinfo->sreflect>0) { /* specular */
line->vect1.x = -line->vect1.x; /* reverse line */
line->vect1.y = -line->vect1.y;
line->vect1.z = -line->vect1.z;
Reflect(&refl,&(line->vect1),norm);
t1 = -DotProd(refl,(oline->vect1));
if (t1>0) {
t2 = pow(t1,cinfo->sreflect)* /* this is slow !! */
lamp->vect1.y/sqrt(dist);
t2 *= (float)cinfo->reflect/(float)CNUM;
color->r+=(short)(t2 * atten.x * (lamp->cinfo.amb.r));
color->g+=(short)(t2 * atten.y * (lamp->cinfo.amb.g));
color->b+=(short)(t2 * atten.z * (lamp->cinfo.amb.b));
}
}
}
lamp=lamp->nextobj;
}
# ifdef RAYDEBUG
printf("DIFFCOLOR: r=%d, g=%d, b=%d\n",
color->r, color->g, color->b);
# endif
free(line);
}
/**********************************************************
Ambient color module
Really simple - just add the color, no questions asked.
**********************************************************/
AmbColor(color, cinfo, norm, loc)
SVECT_PTR color;
VECT_PTR norm, loc;
CINFO_PTR cinfo;
{
color->r+=(short)((int)cinfo->amb.r*(int)cinfo->diff.r/CNUM);
color->g+=(short)((int)cinfo->amb.g*(int)cinfo->diff.g/CNUM);
color->b+=(short)((int)cinfo->amb.b*(int)cinfo->diff.b/CNUM);
# ifdef RAYDEBUG
printf("AMBCOLOR: added r=%d, g=%d, b=%d\n",
cinfo->amb.r,cinfo->amb.g,cinfo->amb.b);
# endif
}
/**********************************************************
Transmitted color module
This is hairy and slow - bends ray around normal vector
by ratio of indicies of refraction. line->flag=TRUE
if we are inside a piece of glass - this means glass must
have a simple structure.
10 Aug 88 - Fixed TransColor so that it works. The
original algorithm came from a book, and I had some
problems getting it to work. This one came from my head,
and seems to work ok.
11 Aug 88 - Added density effects (light is reduced
more by thicker glass).
**********************************************************/
/* #define TESTTRANS TRUE */
TransColor(color,cinfo,norm,loc,line,inmult)
SVECT_PTR color;
VECT_PTR norm,loc;
OBJ_PTR line;
CINFO_PTR cinfo;
float inmult;
{
OBJ_STRUCT newline;
SVECTOR col1;
VECTOR toadd, negvect1, distance;
float index2, multiplier, side1;
int maxtrans;
/* these vars are used to keep track of the distance
traveled through the glass so we can attenuate the
light accordingly. */
static VECTOR prevpos;
VECTOR displacement;
SVECTOR attenuation;
float glassdist;
# ifdef TRANSDEBUG
printf("TRANSCOLOR:\n");
# endif
# ifdef RAYDEBUG
printf("TRANSCOLOR:\n");
# endif
if ((cinfo->trans.r < def.ithreshold) &&
(cinfo->trans.g < def.ithreshold) &&
(cinfo->trans.b < def.ithreshold)) return;
# ifdef ROBUST
if (line->type!=LINE) Error(INTERNAL_ERROR,602);
if ((THEWORLD.globindex == 0) ||
(cinfo->index == 0))
Error(ZERO_INDEX,603);
# endif
if (line->flag) {
/* inside an object going out ? */
index2 = cinfo->index/THEWORLD.globindex;
/* find offset from prev position */
VecSubtract(&displacement,loc,&prevpos);
/* measure distance */
glassdist = sqrt(DotProd(displacement,displacement));
} else {
/* outside an object going in ? */
index2 = THEWORLD.globindex/cinfo->index;
/* remember this position */
VectEQ(&prevpos,loc);
}
/* doesn't currently use new_line() call */
newline.type = LINE;
newline.child = newline.nextobj = NULL;
/* this ray starts from where we hit glass */
VectEQ(&(newline.loc),loc);
VectEqZero(&toadd);
VectNegate(&negvect1,&(line->vect1));
side1 = DotProd((*norm),negvect1);
VectAddMult(&toadd,-side1,norm,(float)1,&negvect1);
VectScale(&toadd,(1-index2));
VectorAdd(&(newline.vect1),&(line->vect1),&toadd);
Normalize(&(newline.vect1));
maxtrans = MAX(MAX(cinfo->trans.r,cinfo->trans.g),
cinfo->trans.b);
/* if we were inside, now we're out */
newline.flag = !(line->flag);
# ifdef TESTTRANS
if (line->flag) printf("line TRUE\n");
if (newline.flag) printf("newline TRUE\n");
printf("Trans = %d %d %d\n",cinfo->trans.r,
cinfo->trans.g,
cinfo->trans.b);
printf("index2 = %f\n",index2);
printf("side1 = %f\n",side1);
printf("Dot(toadd,norm) = %f\n",
DotProd(toadd,(*norm)));
printf("Dot(norm,negvect1) = %f\n",
DotProd((*norm),negvect1));
printf("Dot(-norm,newvect) = %f\n",
-DotProd((*norm),newline.vect1));
printf("inmult, maxtrans = %f %d\n",inmult,maxtrans);
# endif
multiplier = inmult * (float)maxtrans/(float)CNUM;
Ray_Trace(&newline,&col1,multiplier);
THEWORLD.refl_trans++;
# ifdef TESTTRANS
printf("Col1 = %d %d %d\n",col1.r,col1.g,col1.b);
# endif
color->r += col1.r;
color->g += col1.g;
color->b += col1.b;
if (line->flag) { /* density effects? */
/* density uses x,y,z not r,g,b cuz its a floating
point vector. */
attenuation.r = (int)(cinfo->density.x * glassdist *
(float)(color->r));
attenuation.g = (int)(cinfo->density.y * glassdist *
(float)(color->g));
attenuation.b = (int)(cinfo->density.z * glassdist *
(float)(color->b));
/* don't remove more than original intensity! */
color->r -= MIN(attenuation.r,color->r);
color->g -= MIN(attenuation.g,color->g);
color->b -= MIN(attenuation.b,color->b);
}
}
/**********************************************************
Reflective color module
Bounce ray off object and recursively ray trace.
**********************************************************/
ReflectColor(color,cinfo,norm,loc,line,inmult)
SVECT_PTR color;
VECT_PTR norm,loc;
OBJ_PTR line;
CINFO_PTR cinfo;
float inmult;
{
SVECTOR col1;
OBJ_STRUCT newline;
float multiplier;
int maxmirror;
# ifdef ROBUST
if (line->type!=LINE) Error(INTERNAL_ERROR,604);
# endif
if ((cinfo->mirror.r < def.ithreshold) &&
(cinfo->mirror.g < def.ithreshold) &&
(cinfo->mirror.b < def.ithreshold)) return;
newline.type = LINE;
newline.child = newline.nextobj = NULL;
VectEQ(&(newline.loc),loc);
Reflect(&(newline.vect1),&(line->vect1),norm);
maxmirror = MAX(MAX(cinfo->mirror.r,cinfo->mirror.g),
cinfo->mirror.b);
multiplier = inmult * (float)maxmirror/CNUM;
Ray_Trace(&newline,&col1,multiplier);
THEWORLD.refl_trans++;
color->r+=(int)col1.r*(int)cinfo->mirror.r/CNUM;
color->g+=(int)col1.g*(int)cinfo->mirror.g/CNUM;
color->b+=(int)col1.b*(int)cinfo->mirror.b/CNUM;
# ifdef RAYDEBUG
printf("REFLECTCOLOR:\n");
printf(" col1 = %d %d %d\n",col1.r,col1.g,col1.b);
# endif
}
/**********************************************************
Computes sky color given line - interpolate between
horizon and zenith to find color (user should dither
the explitive out of the sky to compensate for lack
of color resolution.
**********************************************************/
SkyColor(line,color)
OBJ_PTR line;
SVECT_PTR color;
{
float length, horiz, zenith;
# ifdef ROBUST
if (line->type!=LINE) Error(INTERNAL_ERROR,605);
# endif
if (THEWORLD.sky==NULL) return;
# ifdef ROBUST
if (THEWORLD.sky->type != SKY)
Error(INTERNAL_ERROR,606);
# endif
length = DotProd(line->vect1,line->vect1);
zenith = sqr(line->vect1.y)/length;
horiz = (sqr(line->vect1.x)+sqr(line->vect1.z))/length;
color->r += (short)(zenith*THEWORLD.skycolor_zenith.r+
horiz *THEWORLD.skycolor_horiz.r);
color->g += (short)(zenith*THEWORLD.skycolor_zenith.g+
horiz *THEWORLD.skycolor_horiz.g);
color->b += (short)(zenith*THEWORLD.skycolor_zenith.b+
horiz *THEWORLD.skycolor_horiz.b);
Dither(color,&((THEWORLD.sky)->cinfo));
# ifdef RAYDEBUG
printf("SKYCOLOR:\n");
# endif
}
/**********************************************************
Color dithering rountine - negative dither number will
dither all three colors together - positive with perform
separate color dithering on all colors.
**********************************************************/
#define MINCOL 10
Dither(color,cinfo)
SVECT_PTR color;
CINFO_PTR cinfo;
{
register int r,g,b;
int PsRand();
if (cinfo->dither==0) return;
if (cinfo->dither>0) {
r=PsRand(); g=PsRand(); b=PsRand();
if (color->r<MINCOL) r=IABS(r);
if (color->g<MINCOL) g=IABS(g);
if (color->b<MINCOL) b=IABS(b);
color->r += (short)(r*cinfo->dither/CNUM);
color->g += (short)(g*cinfo->dither/CNUM);
color->b += (short)(b*cinfo->dither/CNUM);
} else {
r=PsRand();
if ((color->r+color->g+color->b)>(3*MINCOL)) r=IABS(r);
color->r += (short)(r*cinfo->dither/CNUM);
color->g += (short)(r*cinfo->dither/CNUM);
color->b += (short)(r*cinfo->dither/CNUM);
}
}
/**********************************************************
Returns pointer to object hit by ray.
Parameters:
CurrObj = root of object tree
line = light ray
MinT = parameter T for line/obj intersection
sflag = if TRUE, stop on first intersection
fflag = ALWAYS pass TRUE here.
pos1 = position vector for object relative coords
pos2 = position vector for object relative coords
- changed 11 jun 88 to fix shadow routine -
- changed 12 aug 88 to add light attenuation by glass.
this is not done correctly, but is better
than nothing.
**********************************************************/
OBJ_PTR Ray_Hit(CurrObj,line,MinT,sflag,fflag,atten)
OBJ_PTR line, CurrObj;
float *MinT;
short sflag,fflag;
VECT_PTR atten; /* light attenuation by glass */
{
static OBJ_PTR MinObj;
VECTOR loc;
OBJ_PTR obj;
short collision;
static short stop;
float t;
obj = CurrObj;
if (fflag) {
# ifdef ROBUST
if (line->type!=LINE) Error(INTERNAL_ERROR,607);
# endif
*MinT=3e30;
MinObj=NULL;
stop=FALSE;
}
while (obj!=NULL && !stop) { /* check for object collisions */
collision =
(*(ObjData[obj->type].ColTest))(line,obj,&t);
if (collision && (obj->remove != NULL)) {
FindPos(&loc,line,t);
if (Find_Color(obj,obj->remove,&loc,NULL, 1.0, 1.0))
collision = FALSE;
}
THEWORLD.intersect_tests++;
if (collision && (t>SMALL)) { /* did we hit something ? */
# ifdef RAYDEBUG
printf("RAY_HIT: Collision t=%f\n",t);
# endif
if (obj->type!=BBOX) { /* if not BBOX */
THEWORLD.ray_intersects++;
if (sflag && t<1) {
/* did we hit a transparent object? */
/* PS - this is not right - fix it later */
if ((obj->cinfo.trans.r < def.ithreshold) &&
(obj->cinfo.trans.g < def.ithreshold) &&
(obj->cinfo.trans.b < def.ithreshold)) {
stop = TRUE;
MinObj = obj;
return(obj);
} else {
/* attenuate light if transparent object. This is
REALLY screwy and not at all correct, but it is
the only simple way to do it.
*/
if (atten != NULL) {
atten->x *= sqr(((float)obj->cinfo.trans.r) /
(float)CNUM);
atten->y *= sqr(((float)obj->cinfo.trans.g) /
(float)CNUM);
atten->z *= sqr(((float)obj->cinfo.trans.b) /
(float)CNUM);
}
}
}
if ((!sflag) && (t<*MinT)) { /* nearest collision ? */
*MinT = t; /* if so, save it */
MinObj = obj;
}
} else { /* is bbox hit */
THEWORLD.bbox_intersects++;
Ray_Hit(obj->child,line,MinT,sflag,FALSE,atten);
}
}
obj=obj->nextobj;
}
return(MinObj);
}
/**********************************************************
Performs ray tracing in line, fills color structure.
Multiplier is a number by which the color output will
be mulplied (0..1) so that we can tell when its useless
to continue recursivly tracing rays.
**********************************************************/
int Ray_Trace(line,color,multiplier)
OBJ_PTR line;
SVECT_PTR color;
float multiplier;
{
float MinT, divisor;
OBJ_PTR MinObj;
CINFO cinfo;
VECTOR MinLoc, MinNorm;
# ifdef ROBUST
if (line->type!=LINE) Error(INTERNAL_ERROR,608);
# endif
color->r = color->g = color->b = 0;
/* check here if so little light is added that it doesn't matter */
if (multiplier < def.threshold) return(FALSE);
MinObj=Ray_Hit(THEWORLD.stack,line,&MinT,FALSE,TRUE,NULL);
if (MinObj!=NULL) {
FindPos(&MinLoc,line,MinT);
# ifdef RAYDEBUG
printf("RAY_TRACE: Collision, x,y,z = %f %f %f\n",
MinLoc.x, MinLoc.y, MinLoc.z);
# endif
/* Find object normal vector */
(*(ObjData[MinObj->type].FindNorm))(&MinNorm,MinObj,&MinLoc);
if (DotProd(MinNorm,(line->vect1)) >0) { /* reverse */
MinNorm.x = -MinNorm.x; /* normal if */
MinNorm.y = -MinNorm.y; /* necessary */
MinNorm.z = -MinNorm.z;
}
/* Find colinfo */
Find_Color(MinObj,
MinObj->pattern,
&MinLoc,
&cinfo,
MinObj->xmult,
MinObj->ymult
);
AmbColor(color,&cinfo,&MinNorm,&MinLoc);
DiffColor(color,&cinfo,&MinNorm,&MinLoc,line);
TransColor(color,&cinfo,&MinNorm,&MinLoc,line,multiplier);
ReflectColor(color,&cinfo,&MinNorm,&MinLoc,line,multiplier);
Dither(color,&cinfo);
} else {
SkyColor(line,color);
}
if (color->r > CNUM || color->g > CNUM || color->b > CNUM) {
divisor = (float)MAX(MAX(color->r,color->g),color->b) /
(float)CNUM;
color->r = (float)color->r/divisor;
color->g = (float)color->g/divisor;
color->b = (float)color->b/divisor;
}
return(MinObj!=NULL);
}
/**********************************************************
Generates line for a given x,y pixel position
This fn needs a little work, as currently it
produces some distortion around the edge of
the screen.
**********************************************************/
PixelLine(x, y, line) /* generate equation for a line */
int x,y;
OBJ_PTR line;
{
register float xf, yf;
# ifdef ROBUST
if (line->type!=LINE) Error(INTERNAL_ERROR,609);
# endif
xf=((float)(CENTERX-x))/(THEWORLD.flength*ASPECT);
yf=((float)(CENTERY-y))/(THEWORLD.flength);
VectEQ(&(line->loc),&(THEWORLD.observer->loc));
VectEQ(&(line->vect1),&(THEWORLD.observer->vect1));
VectAddMult(&(line->vect1),xf,&(THEWORLD.obsright),
yf,&(THEWORLD.obsup));
line->flag = FALSE; /* redundant? */
}
/**********************************************************
Not yet implimented
**********************************************************/
Region_Trace(xbegin,xend,ybegin,yend)
int xbegin, xend, ybegin, yend;
{
int x,y;
}
/**********************************************************
Sends scan line color data to file - file format is:
BYTE 0 : low byte of x resolution
BYTE 1 : high byte of x resolution
BYTE 2 : low byte of y resolution
BYTE 3 : high byte of y resolution
Then, for each scan line, the line number is listed
(2 bytes), followed by the r,g, and b bytes.
**********************************************************/
Dump_Line(lineno,r,g,b)
short r[],g[],b[];
int lineno;
{
int x;
#ifndef RAYDEBUG
/** line number **/
fputc(((unsigned char)(lineno&(0xff))),THEWORLD.filept);
fputc(((unsigned char)(lineno>>8)), THEWORLD.filept);
for (x=0; x<XSIZE; x++) {
fputc((unsigned char)r[x],THEWORLD.filept);
}
for (x=0; x<XSIZE; x++) {
fputc((unsigned char)g[x],THEWORLD.filept);
}
for (x=0; x<XSIZE; x++) {
fputc((unsigned char)b[x],THEWORLD.filept);
}
#endif
}
/**********************************************************
Ray trace whole screen
**********************************************************/
Screen_Trace() {
int x,y;
short rbyte[XSIZE], gbyte[XSIZE], bbyte[XSIZE];
SVECTOR color;
OBJ_PTR line;
line=new_line();
# ifdef RAYDEBUG
printf("SCREEN_TRACE: line:\n");
printf("First,Last = %d %d\n",
THEWORLD.first_scan,THEWORLD.last_scan);
# endif
for (y=THEWORLD.first_scan; y<=THEWORLD.last_scan; y++) {
for (x=0; x<XSIZE; x++) {
PixelLine(x,y,line);
Ray_Trace(line,&color,(float)1.0);
THEWORLD.primary_traced++;
# ifdef RAYDEBUG
printf("SCREEN_TRACE: r,g,b = %d %d %d\n",
color.r,color.g,color.b);
# endif
rbyte[x] = color.r;
gbyte[x] = color.g;
bbyte[x] = color.b;
# ifdef RAYDEBUG
printf("SCREEN_TRACE: r=%d, g=%d, b=%d\n",
color.r,color.g,color.b);
# endif
}
Dump_Line(y,rbyte,gbyte,bbyte);
}
free(line);
}